Skip to content

Commit 69c5734

Browse files
committed
fixup! Use new context
1 parent 0683412 commit 69c5734

File tree

1 file changed

+159
-0
lines changed

1 file changed

+159
-0
lines changed

site/src/contexts/ProxyContext.tsx

+159
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import { Region } from "api/typesGenerated"
2+
import { useDashboard } from "components/Dashboard/DashboardProvider"
3+
import { createContext, FC, PropsWithChildren, useContext, useState } from "react"
4+
5+
interface ProxyContextValue {
6+
value: PreferredProxy
7+
setValue: (regions: Region[], selectedRegion: Region | undefined) => void
8+
}
9+
10+
interface PreferredProxy {
11+
// Regions is a list of all the regions returned by coderd.
12+
regions: Region[]
13+
// SelectedRegion is the region the user has selected.
14+
selectedRegion: Region | undefined
15+
// PreferredPathAppURL is the URL of the proxy or it is the empty string
16+
// to indicte using relative paths. To add a path to this:
17+
// PreferredPathAppURL + "/path/to/app"
18+
preferredPathAppURL: string
19+
// PreferredWildcardHostname is a hostname that includes a wildcard.
20+
// TODO: If the preferred proxy does not have this set, should we default to'
21+
// the primary's?
22+
// Example: "*.example.com"
23+
preferredWildcardHostname: string
24+
}
25+
26+
const ProxyContext = createContext<ProxyContextValue | undefined>(undefined)
27+
28+
/**
29+
* ProxyProvider interacts with local storage to indicate the preferred workspace proxy.
30+
*/
31+
export const ProxyProvider: FC<PropsWithChildren> = ({ children }) => {
32+
// Try to load the preferred proxy from local storage.
33+
let savedProxy = loadPreferredProxy()
34+
if (!savedProxy) {
35+
savedProxy = getURLs([], undefined)
36+
}
37+
38+
// The initial state is no regions and no selected region.
39+
const [state, setState] = useState<PreferredProxy>(savedProxy)
40+
41+
// ******************************* //
42+
// ** This code can be removed **
43+
// ** when the experimental is **
44+
// ** dropped ** //
45+
const dashboard = useDashboard()
46+
// If the experiment is disabled, then make the setState do a noop.
47+
// This preserves an empty state, which is the default behavior.
48+
if (!dashboard?.experiments.includes("moons")) {
49+
return (
50+
<ProxyContext.Provider value={{
51+
value: getURLs([], undefined),
52+
setValue: () => {
53+
// Does a noop
54+
},
55+
}}>
56+
{children}
57+
</ProxyContext.Provider >
58+
)
59+
}
60+
// ******************************* //
61+
62+
// TODO: @emyrk Should make an api call to /regions endpoint to update the
63+
// regions list.
64+
65+
return (
66+
<ProxyContext.Provider value={{
67+
value: state,
68+
// A function that takes the new regions and selected region and updates
69+
// the state with the appropriate urls.
70+
setValue: (regions, selectedRegion) => {
71+
const preferred = getURLs(regions, selectedRegion)
72+
// Save to local storage to persist the user's preference across reloads
73+
// and other tabs.
74+
savePreferredProxy(preferred)
75+
// Set the state for the current context.
76+
setState(preferred)
77+
},
78+
}}>
79+
{children}
80+
</ProxyContext.Provider >
81+
)
82+
}
83+
84+
export const useProxy = (): ProxyContextValue => {
85+
const context = useContext(ProxyContext)
86+
87+
if (!context) {
88+
throw new Error("useProxy should be used inside of <ProxyProvider />")
89+
}
90+
91+
return context
92+
}
93+
94+
95+
/**
96+
* getURLs is a helper function to calculate the urls to use for a given proxy configuration. By default, it is
97+
* assumed no proxy is configured and relative paths should be used.
98+
*
99+
* @param regions Is the list of regions returned by coderd. If this is empty, default behavior is used.
100+
* @param selectedRegion Is the region the user has selected. If this is undefined, default behavior is used.
101+
*/
102+
const getURLs = (regions: Region[], selectedRegion: Region | undefined): PreferredProxy => {
103+
// By default we set the path app to relative and disable wilcard hostnames.
104+
// We will set these values if we find a proxy we can use that supports them.
105+
let pathAppURL = ""
106+
let wildcardHostname = ""
107+
108+
if (selectedRegion === undefined) {
109+
// If no region is selected, default to the primary region.
110+
selectedRegion = regions.find((region) => region.name === "primary")
111+
} else {
112+
// If a region is selected, make sure it is in the list of regions. If it is not
113+
// we should default to the primary.
114+
selectedRegion = regions.find((region) => region.id === selectedRegion?.id)
115+
}
116+
117+
// Only use healthy regions.
118+
if (selectedRegion && selectedRegion.healthy) {
119+
// By default use relative links for the primary region.
120+
// This is the default, and we should not change it.
121+
if (selectedRegion.name !== "primary") {
122+
pathAppURL = selectedRegion.path_app_url
123+
}
124+
wildcardHostname = selectedRegion.wildcard_hostname
125+
}
126+
127+
// TODO: @emyrk Should we notify the user if they had an unhealthy region selected?
128+
129+
130+
return {
131+
regions: regions,
132+
selectedRegion: selectedRegion,
133+
// Trim trailing slashes to be consistent
134+
preferredPathAppURL: pathAppURL.replace(/\/$/, ""),
135+
preferredWildcardHostname: wildcardHostname,
136+
}
137+
}
138+
139+
// Local storage functions
140+
141+
export const savePreferredProxy = (saved: PreferredProxy): void => {
142+
window.localStorage.setItem("preferred-proxy", JSON.stringify(saved))
143+
}
144+
145+
export const loadPreferredProxy = (): PreferredProxy | undefined => {
146+
const str = localStorage.getItem("preferred-proxy")
147+
if (str === undefined || str === null) {
148+
return undefined
149+
}
150+
const proxy = JSON.parse(str)
151+
if (proxy.id === undefined || proxy.id === null) {
152+
return undefined
153+
}
154+
return proxy
155+
}
156+
157+
export const clearPreferredProxy = (): void => {
158+
localStorage.removeItem("preferred-proxy")
159+
}

0 commit comments

Comments
 (0)