From ab3f897e14c6747ba29cfb66f502709ccea7d15d Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 13 May 2025 17:28:44 -0500 Subject: [PATCH 01/16] feat: default workspace proxy based on latency Auto select the proxy on first load, then defer to user selection. The auto selected proxy will not update again once set. --- site/src/contexts/ProxyContext.tsx | 34 ++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/site/src/contexts/ProxyContext.tsx b/site/src/contexts/ProxyContext.tsx index 55637e32a3069..ef0f4c87b7a1f 100644 --- a/site/src/contexts/ProxyContext.tsx +++ b/site/src/contexts/ProxyContext.tsx @@ -128,18 +128,30 @@ export const ProxyProvider: FC = ({ children }) => { // updateProxy is a helper function that when called will // update the proxy being used. const updateProxy = useCallback(() => { + const userProxy = loadUserSelectedProxy(); // Update the saved user proxy for the caller. - setUserSavedProxy(loadUserSelectedProxy()); - setProxy( - getPreferredProxy( - proxiesResp ?? [], - loadUserSelectedProxy(), - proxyLatencies, - // Do not auto select based on latencies, as inconsistent latencies can cause this - // to behave poorly. - false, - ), - ); + setUserSavedProxy(userProxy); + + // preferred proxy is the proxy that will be used. + // 1. It first selects from the user selected proxy. + // 2. If no user selected proxy is found, it will select + // the best proxy based on latency. + // 2a. The auto select is saved to local storage. So latency changes will not change + // the selected proxy, unless the user manually changes it from the auto selected. + const preferred = getPreferredProxy( + proxiesResp ?? [], + userProxy, + proxyLatencies, + // Autoselect iff the user has not selected a proxy yet. We will save this to local storage. + // If the user disagrees with the auto selected proxy, they can always change it. + true, + ) + + if(userProxy === undefined) { + setUserSavedProxy(preferred.proxy); + } + + setProxy(preferred); }, [proxiesResp, proxyLatencies]); // This useEffect ensures the proxy to be used is updated whenever the state changes. From fd172b7fb76fe8dc0d8597dbccf12a745c3379ee Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 13 May 2025 17:32:52 -0500 Subject: [PATCH 02/16] always save the proxy --- site/src/contexts/ProxyContext.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/site/src/contexts/ProxyContext.tsx b/site/src/contexts/ProxyContext.tsx index ef0f4c87b7a1f..5df88069ee195 100644 --- a/site/src/contexts/ProxyContext.tsx +++ b/site/src/contexts/ProxyContext.tsx @@ -147,9 +147,10 @@ export const ProxyProvider: FC = ({ children }) => { true, ) - if(userProxy === undefined) { - setUserSavedProxy(preferred.proxy); - } + // Always update the proxy in local storage. We do not want this changing automatically. + // It should be set with latencies on load, and then the user can change it. + // If an unhealthy proxy is selected, it will behave as if the user loaded the page for the first time. + setUserSavedProxy(preferred.proxy); setProxy(preferred); }, [proxiesResp, proxyLatencies]); From 5de9d611e86447f16786f1e3e32cd17c88021c9f Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 13 May 2025 17:53:53 -0500 Subject: [PATCH 03/16] save the first latency report back --- site/src/contexts/ProxyContext.tsx | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/site/src/contexts/ProxyContext.tsx b/site/src/contexts/ProxyContext.tsx index 5df88069ee195..1b6e31205c548 100644 --- a/site/src/contexts/ProxyContext.tsx +++ b/site/src/contexts/ProxyContext.tsx @@ -128,10 +128,7 @@ export const ProxyProvider: FC = ({ children }) => { // updateProxy is a helper function that when called will // update the proxy being used. const updateProxy = useCallback(() => { - const userProxy = loadUserSelectedProxy(); - // Update the saved user proxy for the caller. - setUserSavedProxy(userProxy); - + const userSelectedProxy = loadUserSelectedProxy(); // preferred proxy is the proxy that will be used. // 1. It first selects from the user selected proxy. // 2. If no user selected proxy is found, it will select @@ -140,7 +137,7 @@ export const ProxyProvider: FC = ({ children }) => { // the selected proxy, unless the user manually changes it from the auto selected. const preferred = getPreferredProxy( proxiesResp ?? [], - userProxy, + loadUserSelectedProxy(), proxyLatencies, // Autoselect iff the user has not selected a proxy yet. We will save this to local storage. // If the user disagrees with the auto selected proxy, they can always change it. @@ -150,7 +147,14 @@ export const ProxyProvider: FC = ({ children }) => { // Always update the proxy in local storage. We do not want this changing automatically. // It should be set with latencies on load, and then the user can change it. // If an unhealthy proxy is selected, it will behave as if the user loaded the page for the first time. - setUserSavedProxy(preferred.proxy); + if(proxyLatencies && preferred.proxy && proxyLatencies[preferred.proxy.id]) { + // This stores the first proxy to return a latency report. + saveUserSelectedProxy(preferred.proxy); + // Update the saved user proxy for the caller. + setUserSavedProxy(preferred.proxy); + } else { + setUserSavedProxy(userSelectedProxy); + } setProxy(preferred); }, [proxiesResp, proxyLatencies]); @@ -227,14 +231,16 @@ export const getPreferredProxy = ( // If no proxy is selected, or the selected proxy is unhealthy default to the primary proxy. if (!selectedProxy || !selectedProxy.healthy) { - // By default, use the primary proxy. - selectedProxy = proxies.find((proxy) => proxy.name === "primary"); - // If we have latencies, then attempt to use the best proxy by latency instead. const best = selectByLatency(proxies, latencies); if (autoSelectBasedOnLatency && best) { selectedProxy = best; } + + // Use the primary proxy if we don't have latencies + if(!best) { + selectedProxy = proxies.find((proxy) => proxy.name === "primary"); + } } return computeUsableURLS(selectedProxy); From e0b9eb3f5b2c03b1d564440e304c0da8ca49c499 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 13 May 2025 18:00:34 -0500 Subject: [PATCH 04/16] add comments --- site/src/contexts/ProxyContext.tsx | 75 ++++++++++++++++------------ site/src/contexts/useProxyLatency.ts | 5 ++ 2 files changed, 48 insertions(+), 32 deletions(-) diff --git a/site/src/contexts/ProxyContext.tsx b/site/src/contexts/ProxyContext.tsx index 1b6e31205c548..ebef914c41e4a 100644 --- a/site/src/contexts/ProxyContext.tsx +++ b/site/src/contexts/ProxyContext.tsx @@ -122,41 +122,24 @@ export const ProxyProvider: FC = ({ children }) => { // Every time we get a new proxiesResponse, update the latency check // to each workspace proxy. - const { proxyLatencies, refetch: refetchProxyLatencies } = + const { proxyLatencies, refetch: refetchProxyLatencies, loaded: latenciesLoaded } = useProxyLatency(proxiesResp); // updateProxy is a helper function that when called will // update the proxy being used. const updateProxy = useCallback(() => { - const userSelectedProxy = loadUserSelectedProxy(); - // preferred proxy is the proxy that will be used. - // 1. It first selects from the user selected proxy. - // 2. If no user selected proxy is found, it will select - // the best proxy based on latency. - // 2a. The auto select is saved to local storage. So latency changes will not change - // the selected proxy, unless the user manually changes it from the auto selected. - const preferred = getPreferredProxy( - proxiesResp ?? [], - loadUserSelectedProxy(), - proxyLatencies, - // Autoselect iff the user has not selected a proxy yet. We will save this to local storage. - // If the user disagrees with the auto selected proxy, they can always change it. - true, - ) - - // Always update the proxy in local storage. We do not want this changing automatically. - // It should be set with latencies on load, and then the user can change it. - // If an unhealthy proxy is selected, it will behave as if the user loaded the page for the first time. - if(proxyLatencies && preferred.proxy && proxyLatencies[preferred.proxy.id]) { - // This stores the first proxy to return a latency report. - saveUserSelectedProxy(preferred.proxy); - // Update the saved user proxy for the caller. - setUserSavedProxy(preferred.proxy); - } else { - setUserSavedProxy(userSelectedProxy); - } - - setProxy(preferred); + // Update the saved user proxy for the caller. + setUserSavedProxy(loadUserSelectedProxy()); + setProxy( + getPreferredProxy( + proxiesResp ?? [], + loadUserSelectedProxy(), + proxyLatencies, + // Do not auto select based on latencies, as inconsistent latencies can cause this + // to behave poorly. + false, + ), + ); }, [proxiesResp, proxyLatencies]); // This useEffect ensures the proxy to be used is updated whenever the state changes. @@ -166,6 +149,34 @@ export const ProxyProvider: FC = ({ children }) => { updateProxy(); }, [proxiesResp, proxyLatencies]); + // This useEffect will auto select the best proxy if the user has not selected one. + // It must wait until all latencies are loaded to select based on latency. This does mean + // the first time a user loads the page, the proxy will "flicker" to the best proxy. + // + // Once the page is loaded, or the user selects a proxy, this will not run again. + useEffect(() => { + if(loadUserSelectedProxy() !== undefined) { + return; // User has selected a proxy, do not auto select. + } + if(!latenciesLoaded) { + // Wait until the latencies are loaded before + return; + } + + const best = getPreferredProxy( + proxiesResp ?? [], + loadUserSelectedProxy(), + proxyLatencies, + true, + ); + + if(best?.proxy) { + saveUserSelectedProxy(best.proxy); + updateProxy(); + } + + }, [latenciesLoaded]) + return ( proxy.name === "primary"); } } diff --git a/site/src/contexts/useProxyLatency.ts b/site/src/contexts/useProxyLatency.ts index ff8be8cd66135..cd9fd6b27bd8f 100644 --- a/site/src/contexts/useProxyLatency.ts +++ b/site/src/contexts/useProxyLatency.ts @@ -48,6 +48,7 @@ export const useProxyLatency = ( // Until the new values are loaded, the old values will still be used. refetch: () => Date; proxyLatencies: Record; + loaded: boolean; } => { // maxStoredLatencies is the maximum number of latencies to store per proxy in local storage. let maxStoredLatencies = 1; @@ -73,6 +74,8 @@ export const useProxyLatency = ( new Date(new Date().getTime() - proxyIntervalSeconds * 1000).toISOString(), ); + const [loaded, setLoaded] = useState(false); + // Refetch will always set the latestFetchRequest to the current time, making all the cached latencies // stale and triggering a refetch of all proxies in the list. const refetch = () => { @@ -231,6 +234,7 @@ export const useProxyLatency = ( // Local storage cleanup garbageCollectStoredLatencies(proxies, maxStoredLatencies); + setLoaded(true); }); return () => { @@ -241,6 +245,7 @@ export const useProxyLatency = ( return { proxyLatencies, refetch, + loaded }; }; From 5427b6144b74842f8f608729bbf0a48afc96f9be Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 13 May 2025 18:01:45 -0500 Subject: [PATCH 05/16] unit test --- site/src/contexts/ProxyContext.test.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/site/src/contexts/ProxyContext.test.tsx b/site/src/contexts/ProxyContext.test.tsx index 8e16e868627e3..4ff6cebfae14a 100644 --- a/site/src/contexts/ProxyContext.test.tsx +++ b/site/src/contexts/ProxyContext.test.tsx @@ -26,7 +26,7 @@ import type * as ProxyLatency from "./useProxyLatency"; // here and not inside a unit test. jest.mock("contexts/useProxyLatency", () => ({ useProxyLatency: () => { - return { proxyLatencies: hardCodedLatencies, refetch: jest.fn() }; + return { proxyLatencies: hardCodedLatencies, refetch: jest.fn(), loaded: true }; }, })); @@ -261,11 +261,11 @@ describe("ProxyContextSelection", () => { expUserProxyID: MockHealthyWildWorkspaceProxy.id, }, ], - // Latency behavior is disabled, so the primary should be selected. + // First page load defers to the proxy by latency [ "regions_default_low_latency", { - expProxyID: MockPrimaryWorkspaceProxy.id, + expProxyID: MockHealthyWildWorkspaceProxy.id, regions: MockWorkspaceProxies, storageProxy: undefined, latencies: { From dcf98d78d372330db9f834cfd5836cbe19c4b4c8 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 13 May 2025 18:03:04 -0500 Subject: [PATCH 06/16] comment --- site/src/contexts/ProxyContext.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/site/src/contexts/ProxyContext.tsx b/site/src/contexts/ProxyContext.tsx index ebef914c41e4a..969c1181829c9 100644 --- a/site/src/contexts/ProxyContext.tsx +++ b/site/src/contexts/ProxyContext.tsx @@ -136,7 +136,8 @@ export const ProxyProvider: FC = ({ children }) => { loadUserSelectedProxy(), proxyLatencies, // Do not auto select based on latencies, as inconsistent latencies can cause this - // to behave poorly. + // to change on each call. updateProxy should be stable when selecting a proxy to + // prevent flickering. false, ), ); @@ -150,7 +151,7 @@ export const ProxyProvider: FC = ({ children }) => { }, [proxiesResp, proxyLatencies]); // This useEffect will auto select the best proxy if the user has not selected one. - // It must wait until all latencies are loaded to select based on latency. This does mean + // It must wait until all latencies are loaded to select based on latency. This does mean // the first time a user loads the page, the proxy will "flicker" to the best proxy. // // Once the page is loaded, or the user selects a proxy, this will not run again. From 93bc6b372c107edcb4424ea1fb9df51a53888124 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 13 May 2025 18:13:21 -0500 Subject: [PATCH 07/16] fmt --- site/src/contexts/ProxyContext.test.tsx | 6 +++++- site/src/contexts/ProxyContext.tsx | 21 ++++++++++++--------- site/src/contexts/useProxyLatency.ts | 2 +- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/site/src/contexts/ProxyContext.test.tsx b/site/src/contexts/ProxyContext.test.tsx index 4ff6cebfae14a..addcdb09d0705 100644 --- a/site/src/contexts/ProxyContext.test.tsx +++ b/site/src/contexts/ProxyContext.test.tsx @@ -26,7 +26,11 @@ import type * as ProxyLatency from "./useProxyLatency"; // here and not inside a unit test. jest.mock("contexts/useProxyLatency", () => ({ useProxyLatency: () => { - return { proxyLatencies: hardCodedLatencies, refetch: jest.fn(), loaded: true }; + return { + proxyLatencies: hardCodedLatencies, + refetch: jest.fn(), + loaded: true, + }; }, })); diff --git a/site/src/contexts/ProxyContext.tsx b/site/src/contexts/ProxyContext.tsx index 969c1181829c9..facbc49ffd989 100644 --- a/site/src/contexts/ProxyContext.tsx +++ b/site/src/contexts/ProxyContext.tsx @@ -122,8 +122,11 @@ export const ProxyProvider: FC = ({ children }) => { // Every time we get a new proxiesResponse, update the latency check // to each workspace proxy. - const { proxyLatencies, refetch: refetchProxyLatencies, loaded: latenciesLoaded } = - useProxyLatency(proxiesResp); + const { + proxyLatencies, + refetch: refetchProxyLatencies, + loaded: latenciesLoaded, + } = useProxyLatency(proxiesResp); // updateProxy is a helper function that when called will // update the proxy being used. @@ -153,13 +156,14 @@ export const ProxyProvider: FC = ({ children }) => { // This useEffect will auto select the best proxy if the user has not selected one. // It must wait until all latencies are loaded to select based on latency. This does mean // the first time a user loads the page, the proxy will "flicker" to the best proxy. - // + // // Once the page is loaded, or the user selects a proxy, this will not run again. + // biome-ignore lint/correctness/useExhaustiveDependencies: Only update if the source data changes useEffect(() => { - if(loadUserSelectedProxy() !== undefined) { + if (loadUserSelectedProxy() !== undefined) { return; // User has selected a proxy, do not auto select. } - if(!latenciesLoaded) { + if (!latenciesLoaded) { // Wait until the latencies are loaded before return; } @@ -171,12 +175,11 @@ export const ProxyProvider: FC = ({ children }) => { true, ); - if(best?.proxy) { + if (best?.proxy) { saveUserSelectedProxy(best.proxy); updateProxy(); } - - }, [latenciesLoaded]) + }, [latenciesLoaded, proxiesResp, proxyLatencies]); return ( proxy.name === "primary"); } } diff --git a/site/src/contexts/useProxyLatency.ts b/site/src/contexts/useProxyLatency.ts index cd9fd6b27bd8f..cb92274b8286f 100644 --- a/site/src/contexts/useProxyLatency.ts +++ b/site/src/contexts/useProxyLatency.ts @@ -245,7 +245,7 @@ export const useProxyLatency = ( return { proxyLatencies, refetch, - loaded + loaded, }; }; From f5c0719526802b17cb47956d4b69eb67c593f730 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 13 May 2025 20:03:23 -0500 Subject: [PATCH 08/16] add latenciesLoaded to unit tests --- site/src/contexts/ProxyContext.test.tsx | 10 +++++++--- site/src/contexts/ProxyContext.tsx | 14 ++++++++------ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/site/src/contexts/ProxyContext.test.tsx b/site/src/contexts/ProxyContext.test.tsx index addcdb09d0705..b3050c0cd3fcd 100644 --- a/site/src/contexts/ProxyContext.test.tsx +++ b/site/src/contexts/ProxyContext.test.tsx @@ -119,7 +119,7 @@ describe("ProxyContextGetURLs", () => { preferredPathAppURL, preferredWildcardHostname, ) => { - const preferred = getPreferredProxy(regions, selected, latencies); + const preferred = getPreferredProxy(regions, selected, latencies, true); expect(preferred.preferredPathAppURL).toBe(preferredPathAppURL); expect(preferred.preferredWildcardHostname).toBe( preferredWildcardHostname, @@ -142,10 +142,11 @@ const TestingComponent = () => { // TestingScreen just mounts some components that we can check in the unit test. const TestingScreen = () => { - const { proxy, userProxy, isFetched, isLoading, clearProxy, setProxy } = + const { proxy, userProxy, isFetched, isLoading, latenciesLoaded, clearProxy, setProxy } = useProxy(); return ( <> +
@@ -210,7 +211,6 @@ describe("ProxyContextSelection", () => { }; it.each([ - // Not latency behavior [ "empty", { @@ -366,6 +366,10 @@ describe("ProxyContextSelection", () => { TestingComponent(); await waitForLoaderToBeRemoved(); + await screen.findByTestId("latenciesLoaded").then((x) => { + expect(x.title).toBe("true"); + }); + if (afterLoad) { await afterLoad(); } diff --git a/site/src/contexts/ProxyContext.tsx b/site/src/contexts/ProxyContext.tsx index facbc49ffd989..b003a971ffc28 100644 --- a/site/src/contexts/ProxyContext.tsx +++ b/site/src/contexts/ProxyContext.tsx @@ -54,6 +54,9 @@ export interface ProxyContextValue { // then the latency has not been fetched yet. Calculations happen async for each proxy in the list. // Refer to the returned report for a given proxy for more information. proxyLatencies: ProxyLatencies; + // latenciesLoaded is true when the latencies have been initially loaded. + // Once set to true, it will not be set to false again. + latenciesLoaded: boolean; // refetchProxyLatencies will trigger refreshing of the proxy latencies. By default the latencies // are loaded once. refetchProxyLatencies: () => Date; @@ -189,6 +192,7 @@ export const ProxyProvider: FC = ({ children }) => { userProxy: userSavedProxy, proxy: proxy, proxies: proxiesResp, + latenciesLoaded: latenciesLoaded, isLoading: proxiesLoading, isFetched: proxiesFetched, error: proxiesError, @@ -246,16 +250,14 @@ export const getPreferredProxy = ( // If no proxy is selected, or the selected proxy is unhealthy default to the primary proxy. if (!selectedProxy || !selectedProxy.healthy) { + // Default to the primary proxy + selectedProxy = proxies.find((proxy) => proxy.name === "primary"); + // If we have latencies, then attempt to use the best proxy by latency instead. const best = selectByLatency(proxies, latencies); - if (autoSelectBasedOnLatency && best) { + if (autoSelectBasedOnLatency && best !== undefined) { selectedProxy = best; } - - // Use the primary proxy if we don't have any other options. - if (!selectedProxy) { - selectedProxy = proxies.find((proxy) => proxy.name === "primary"); - } } return computeUsableURLS(selectedProxy); From f2b5d9c4e722e45339954227616aa22e7d75e223 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 13 May 2025 20:07:39 -0500 Subject: [PATCH 09/16] fixup tests --- site/src/contexts/ProxyContext.test.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/site/src/contexts/ProxyContext.test.tsx b/site/src/contexts/ProxyContext.test.tsx index b3050c0cd3fcd..66945ef59b0a4 100644 --- a/site/src/contexts/ProxyContext.test.tsx +++ b/site/src/contexts/ProxyContext.test.tsx @@ -224,6 +224,7 @@ describe("ProxyContextSelection", () => { "regions_no_selection", { expProxyID: MockPrimaryWorkspaceProxy.id, + expUserProxyID: MockPrimaryWorkspaceProxy.id, regions: MockWorkspaceProxies, storageProxy: undefined, }, @@ -270,6 +271,7 @@ describe("ProxyContextSelection", () => { "regions_default_low_latency", { expProxyID: MockHealthyWildWorkspaceProxy.id, + expUserProxyID: MockHealthyWildWorkspaceProxy.id, regions: MockWorkspaceProxies, storageProxy: undefined, latencies: { From b3102b7d6e86b8c2732d770536643400eda1a6b8 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 13 May 2025 20:11:55 -0500 Subject: [PATCH 10/16] fixup test fixtures --- site/src/modules/dashboard/Navbar/MobileMenu.stories.tsx | 1 + site/src/modules/dashboard/Navbar/NavbarView.test.tsx | 1 + site/src/modules/dashboard/Navbar/ProxyMenu.stories.tsx | 1 + site/src/modules/resources/AgentRow.stories.tsx | 2 ++ site/src/modules/resources/AppLink/AppLink.stories.tsx | 1 + site/src/modules/resources/ResourceCard.stories.tsx | 1 + site/src/modules/resources/Resources.stories.tsx | 1 + .../WorkspaceAppStatus/WorkspaceAppStatus.stories.tsx | 1 + site/src/pages/WorkspacePage/AppStatuses.stories.tsx | 1 + site/src/pages/WorkspacePage/Workspace.stories.tsx | 1 + site/src/pages/WorkspacesPage/WorkspacesPageView.stories.tsx | 1 + 11 files changed, 12 insertions(+) diff --git a/site/src/modules/dashboard/Navbar/MobileMenu.stories.tsx b/site/src/modules/dashboard/Navbar/MobileMenu.stories.tsx index 058c8799c95e0..697e444ee2ab6 100644 --- a/site/src/modules/dashboard/Navbar/MobileMenu.stories.tsx +++ b/site/src/modules/dashboard/Navbar/MobileMenu.stories.tsx @@ -28,6 +28,7 @@ const meta: Meta = { preferredWildcardHostname: "", proxy: MockPrimaryWorkspaceProxy, }, + latenciesLoaded: true, isLoading: false, isFetched: true, setProxy: fn(), diff --git a/site/src/modules/dashboard/Navbar/NavbarView.test.tsx b/site/src/modules/dashboard/Navbar/NavbarView.test.tsx index 6739f666c2b17..87f91e8806ecb 100644 --- a/site/src/modules/dashboard/Navbar/NavbarView.test.tsx +++ b/site/src/modules/dashboard/Navbar/NavbarView.test.tsx @@ -11,6 +11,7 @@ const proxyContextValue: ProxyContextValue = { preferredWildcardHostname: "", proxy: MockPrimaryWorkspaceProxy, }, + latenciesLoaded: true, isLoading: false, isFetched: true, setProxy: jest.fn(), diff --git a/site/src/modules/dashboard/Navbar/ProxyMenu.stories.tsx b/site/src/modules/dashboard/Navbar/ProxyMenu.stories.tsx index 6df47684173fe..7669d9830a403 100644 --- a/site/src/modules/dashboard/Navbar/ProxyMenu.stories.tsx +++ b/site/src/modules/dashboard/Navbar/ProxyMenu.stories.tsx @@ -19,6 +19,7 @@ const defaultProxyContextValue = { proxy: getPreferredProxy(MockWorkspaceProxies, undefined), proxies: MockWorkspaceProxies, isLoading: false, + latenciesLoaded: true, isFetched: true, setProxy: fn(), clearProxy: fn(), diff --git a/site/src/modules/resources/AgentRow.stories.tsx b/site/src/modules/resources/AgentRow.stories.tsx index 0e80ee0a5ecd0..5c79a7e3499a2 100644 --- a/site/src/modules/resources/AgentRow.stories.tsx +++ b/site/src/modules/resources/AgentRow.stories.tsx @@ -102,6 +102,7 @@ const meta: Meta = { proxy: getPreferredProxy([], undefined), proxies: [], isLoading: false, + latenciesLoaded: true, isFetched: true, setProxy: () => { return; @@ -263,6 +264,7 @@ export const ShowingPortForward: Story = { ), proxies: M.MockWorkspaceProxies, isLoading: false, + latenciesLoaded: true, isFetched: true, setProxy: () => { return; diff --git a/site/src/modules/resources/AppLink/AppLink.stories.tsx b/site/src/modules/resources/AppLink/AppLink.stories.tsx index 8f710e818aee2..c9d85ab14e65d 100644 --- a/site/src/modules/resources/AppLink/AppLink.stories.tsx +++ b/site/src/modules/resources/AppLink/AppLink.stories.tsx @@ -28,6 +28,7 @@ const meta: Meta = { }, proxies: MockWorkspaceProxies, isLoading: false, + latenciesLoaded: true, isFetched: true, setProxy: () => { return; diff --git a/site/src/modules/resources/ResourceCard.stories.tsx b/site/src/modules/resources/ResourceCard.stories.tsx index 1ce76894a032e..93e4e2de2ea68 100644 --- a/site/src/modules/resources/ResourceCard.stories.tsx +++ b/site/src/modules/resources/ResourceCard.stories.tsx @@ -79,6 +79,7 @@ function getAgentRow(agent: WorkspaceAgent): JSX.Element { proxy: getPreferredProxy([], undefined), proxies: [], isLoading: false, + latenciesLoaded: true, isFetched: true, setProxy: () => { return; diff --git a/site/src/modules/resources/Resources.stories.tsx b/site/src/modules/resources/Resources.stories.tsx index da8ed2f249696..6d54aaf8dd16b 100644 --- a/site/src/modules/resources/Resources.stories.tsx +++ b/site/src/modules/resources/Resources.stories.tsx @@ -175,6 +175,7 @@ function getAgentRow(agent: WorkspaceAgent): JSX.Element { proxy: getPreferredProxy([], undefined), proxies: [], isLoading: false, + latenciesLoaded: true, isFetched: true, setProxy: () => { return; diff --git a/site/src/modules/workspaces/WorkspaceAppStatus/WorkspaceAppStatus.stories.tsx b/site/src/modules/workspaces/WorkspaceAppStatus/WorkspaceAppStatus.stories.tsx index 74ec70a863a08..08ee3e672d2a2 100644 --- a/site/src/modules/workspaces/WorkspaceAppStatus/WorkspaceAppStatus.stories.tsx +++ b/site/src/modules/workspaces/WorkspaceAppStatus/WorkspaceAppStatus.stories.tsx @@ -19,6 +19,7 @@ const meta: Meta = { proxyLatencies: MockProxyLatencies, proxy: getPreferredProxy([], undefined), proxies: [], + latenciesLoaded: true, isLoading: false, isFetched: true, clearProxy: () => { diff --git a/site/src/pages/WorkspacePage/AppStatuses.stories.tsx b/site/src/pages/WorkspacePage/AppStatuses.stories.tsx index 86e6f345b5e59..ca28bd922db14 100644 --- a/site/src/pages/WorkspacePage/AppStatuses.stories.tsx +++ b/site/src/pages/WorkspacePage/AppStatuses.stories.tsx @@ -21,6 +21,7 @@ const meta: Meta = { proxy: getPreferredProxy([], undefined), proxies: [], isLoading: false, + latenciesLoaded: true, isFetched: true, clearProxy: () => { return; diff --git a/site/src/pages/WorkspacePage/Workspace.stories.tsx b/site/src/pages/WorkspacePage/Workspace.stories.tsx index a59e2f78bcee2..9253995299393 100644 --- a/site/src/pages/WorkspacePage/Workspace.stories.tsx +++ b/site/src/pages/WorkspacePage/Workspace.stories.tsx @@ -53,6 +53,7 @@ const meta: Meta = { proxy: getPreferredProxy([], undefined), proxies: [], isLoading: false, + latenciesLoaded: true, isFetched: true, clearProxy: () => { return; diff --git a/site/src/pages/WorkspacesPage/WorkspacesPageView.stories.tsx b/site/src/pages/WorkspacesPage/WorkspacesPageView.stories.tsx index dc1b9c2f4fb82..6f729270af833 100644 --- a/site/src/pages/WorkspacesPage/WorkspacesPageView.stories.tsx +++ b/site/src/pages/WorkspacesPage/WorkspacesPageView.stories.tsx @@ -157,6 +157,7 @@ const meta: Meta = { proxy: getPreferredProxy([], undefined), proxies: [], isLoading: false, + latenciesLoaded: true, isFetched: true, clearProxy: () => { return; From d07e4727807a506c5467ad7fb1c0ecd2da9df979 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Wed, 14 May 2025 07:14:56 -0500 Subject: [PATCH 11/16] fmt --- site/src/contexts/ProxyContext.test.tsx | 18 +++++++++++++++--- site/src/contexts/ProxyContext.tsx | 2 +- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/site/src/contexts/ProxyContext.test.tsx b/site/src/contexts/ProxyContext.test.tsx index 66945ef59b0a4..096bc1a8f9b5c 100644 --- a/site/src/contexts/ProxyContext.test.tsx +++ b/site/src/contexts/ProxyContext.test.tsx @@ -142,11 +142,23 @@ const TestingComponent = () => { // TestingScreen just mounts some components that we can check in the unit test. const TestingScreen = () => { - const { proxy, userProxy, isFetched, isLoading, latenciesLoaded, clearProxy, setProxy } = - useProxy(); + const { + proxy, + userProxy, + isFetched, + isLoading, + latenciesLoaded, + clearProxy, + setProxy, + } = useProxy(); + + console.log(proxy); return ( <> -
+
diff --git a/site/src/contexts/ProxyContext.tsx b/site/src/contexts/ProxyContext.tsx index b003a971ffc28..c526f1b0f3a67 100644 --- a/site/src/contexts/ProxyContext.tsx +++ b/site/src/contexts/ProxyContext.tsx @@ -54,7 +54,7 @@ export interface ProxyContextValue { // then the latency has not been fetched yet. Calculations happen async for each proxy in the list. // Refer to the returned report for a given proxy for more information. proxyLatencies: ProxyLatencies; - // latenciesLoaded is true when the latencies have been initially loaded. + // latenciesLoaded is true when the latencies have been initially loaded. // Once set to true, it will not be set to false again. latenciesLoaded: boolean; // refetchProxyLatencies will trigger refreshing of the proxy latencies. By default the latencies From abf49ae684afe665829cd0efbf64bc84423641db Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Wed, 14 May 2025 07:19:24 -0500 Subject: [PATCH 12/16] remove console log, whoops --- site/src/contexts/ProxyContext.test.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/site/src/contexts/ProxyContext.test.tsx b/site/src/contexts/ProxyContext.test.tsx index 096bc1a8f9b5c..03f2662037733 100644 --- a/site/src/contexts/ProxyContext.test.tsx +++ b/site/src/contexts/ProxyContext.test.tsx @@ -152,7 +152,6 @@ const TestingScreen = () => { setProxy, } = useProxy(); - console.log(proxy); return ( <>
Date: Wed, 14 May 2025 07:27:44 -0500 Subject: [PATCH 13/16] add comments --- site/src/contexts/ProxyContext.tsx | 2 +- site/src/contexts/useProxyLatency.ts | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/site/src/contexts/ProxyContext.tsx b/site/src/contexts/ProxyContext.tsx index c526f1b0f3a67..c162c2c4952ff 100644 --- a/site/src/contexts/ProxyContext.tsx +++ b/site/src/contexts/ProxyContext.tsx @@ -167,7 +167,7 @@ export const ProxyProvider: FC = ({ children }) => { return; // User has selected a proxy, do not auto select. } if (!latenciesLoaded) { - // Wait until the latencies are loaded before + // Wait until the latencies are loaded first. return; } diff --git a/site/src/contexts/useProxyLatency.ts b/site/src/contexts/useProxyLatency.ts index cb92274b8286f..f5f3d2acb415c 100644 --- a/site/src/contexts/useProxyLatency.ts +++ b/site/src/contexts/useProxyLatency.ts @@ -48,6 +48,10 @@ export const useProxyLatency = ( // Until the new values are loaded, the old values will still be used. refetch: () => Date; proxyLatencies: Record; + // loaded signals all latency requests have completed. Once set to true, this will not change. + // Latencies at this point should be loaded from local storage, and updated asynchronously as needed. + // If local storage has updated latencies, then this will be set to true with 0 actual network requests. + // The loaded latencies will all be from the cache. loaded: boolean; } => { // maxStoredLatencies is the maximum number of latencies to store per proxy in local storage. From 9d9e7f3e45460e1bce79c7f267666c893e8ebe19 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 27 May 2025 13:36:45 -0500 Subject: [PATCH 14/16] revert to main --- site/src/modules/dashboard/Navbar/MobileMenu.stories.tsx | 1 - site/src/modules/dashboard/Navbar/NavbarView.test.tsx | 1 - site/src/modules/dashboard/Navbar/ProxyMenu.stories.tsx | 1 - 3 files changed, 3 deletions(-) diff --git a/site/src/modules/dashboard/Navbar/MobileMenu.stories.tsx b/site/src/modules/dashboard/Navbar/MobileMenu.stories.tsx index 697e444ee2ab6..058c8799c95e0 100644 --- a/site/src/modules/dashboard/Navbar/MobileMenu.stories.tsx +++ b/site/src/modules/dashboard/Navbar/MobileMenu.stories.tsx @@ -28,7 +28,6 @@ const meta: Meta = { preferredWildcardHostname: "", proxy: MockPrimaryWorkspaceProxy, }, - latenciesLoaded: true, isLoading: false, isFetched: true, setProxy: fn(), diff --git a/site/src/modules/dashboard/Navbar/NavbarView.test.tsx b/site/src/modules/dashboard/Navbar/NavbarView.test.tsx index 87f91e8806ecb..6739f666c2b17 100644 --- a/site/src/modules/dashboard/Navbar/NavbarView.test.tsx +++ b/site/src/modules/dashboard/Navbar/NavbarView.test.tsx @@ -11,7 +11,6 @@ const proxyContextValue: ProxyContextValue = { preferredWildcardHostname: "", proxy: MockPrimaryWorkspaceProxy, }, - latenciesLoaded: true, isLoading: false, isFetched: true, setProxy: jest.fn(), diff --git a/site/src/modules/dashboard/Navbar/ProxyMenu.stories.tsx b/site/src/modules/dashboard/Navbar/ProxyMenu.stories.tsx index 7669d9830a403..6df47684173fe 100644 --- a/site/src/modules/dashboard/Navbar/ProxyMenu.stories.tsx +++ b/site/src/modules/dashboard/Navbar/ProxyMenu.stories.tsx @@ -19,7 +19,6 @@ const defaultProxyContextValue = { proxy: getPreferredProxy(MockWorkspaceProxies, undefined), proxies: MockWorkspaceProxies, isLoading: false, - latenciesLoaded: true, isFetched: true, setProxy: fn(), clearProxy: fn(), From 6036996a80208448353c268639e39be33b01d993 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 27 May 2025 13:37:44 -0500 Subject: [PATCH 15/16] fix js test --- site/src/testHelpers/storybook.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/site/src/testHelpers/storybook.tsx b/site/src/testHelpers/storybook.tsx index ed64c10958a0b..4b2ba94bd2577 100644 --- a/site/src/testHelpers/storybook.tsx +++ b/site/src/testHelpers/storybook.tsx @@ -167,6 +167,7 @@ export const withProxyProvider = return ( Date: Tue, 27 May 2025 15:41:04 -0500 Subject: [PATCH 16/16] fix js test --- site/src/modules/dashboard/Navbar/MobileMenu.stories.tsx | 1 + site/src/modules/dashboard/Navbar/NavbarView.test.tsx | 1 + site/src/modules/dashboard/Navbar/ProxyMenu.stories.tsx | 1 + 3 files changed, 3 insertions(+) diff --git a/site/src/modules/dashboard/Navbar/MobileMenu.stories.tsx b/site/src/modules/dashboard/Navbar/MobileMenu.stories.tsx index 058c8799c95e0..cb186dcb973b0 100644 --- a/site/src/modules/dashboard/Navbar/MobileMenu.stories.tsx +++ b/site/src/modules/dashboard/Navbar/MobileMenu.stories.tsx @@ -23,6 +23,7 @@ const meta: Meta = { component: MobileMenu, args: { proxyContextValue: { + latenciesLoaded: true, proxy: { preferredPathAppURL: "", preferredWildcardHostname: "", diff --git a/site/src/modules/dashboard/Navbar/NavbarView.test.tsx b/site/src/modules/dashboard/Navbar/NavbarView.test.tsx index 6739f666c2b17..358b717b492a4 100644 --- a/site/src/modules/dashboard/Navbar/NavbarView.test.tsx +++ b/site/src/modules/dashboard/Navbar/NavbarView.test.tsx @@ -6,6 +6,7 @@ import { renderWithAuth } from "testHelpers/renderHelpers"; import { NavbarView } from "./NavbarView"; const proxyContextValue: ProxyContextValue = { + latenciesLoaded: true, proxy: { preferredPathAppURL: "", preferredWildcardHostname: "", diff --git a/site/src/modules/dashboard/Navbar/ProxyMenu.stories.tsx b/site/src/modules/dashboard/Navbar/ProxyMenu.stories.tsx index 6df47684173fe..15dbb18471c3f 100644 --- a/site/src/modules/dashboard/Navbar/ProxyMenu.stories.tsx +++ b/site/src/modules/dashboard/Navbar/ProxyMenu.stories.tsx @@ -15,6 +15,7 @@ import { withDesktopViewport } from "testHelpers/storybook"; import { ProxyMenu } from "./ProxyMenu"; const defaultProxyContextValue = { + latenciesLoaded: true, proxyLatencies: MockProxyLatencies, proxy: getPreferredProxy(MockWorkspaceProxies, undefined), proxies: MockWorkspaceProxies,