|
1 | 1 | import{css,typeInterpolation,typeTheme,useTheme}from"@emotion/react";
|
2 |
| -importKeyboardArrowDownOutlinedfrom"@mui/icons-material/KeyboardArrowDownOutlined"; |
3 | 2 | importMenuIconfrom"@mui/icons-material/Menu";
|
4 |
| -importButtonfrom"@mui/material/Button"; |
5 |
| -importDividerfrom"@mui/material/Divider"; |
6 | 3 | importDrawerfrom"@mui/material/Drawer";
|
7 | 4 | importIconButtonfrom"@mui/material/IconButton";
|
8 |
| -importMenufrom"@mui/material/Menu"; |
9 |
| -importMenuItemfrom"@mui/material/MenuItem"; |
10 |
| -importSkeletonfrom"@mui/material/Skeleton"; |
11 |
| -import{visuallyHidden}from"@mui/utils"; |
12 |
| -import{typeFC,useRef,useState}from"react"; |
13 |
| -import{NavLink,useLocation,useNavigate}from"react-router-dom"; |
| 5 | +import{typeFC,useState}from"react"; |
| 6 | +import{NavLink,useLocation}from"react-router-dom"; |
14 | 7 | importtype*asTypesGenfrom"api/typesGenerated";
|
15 |
| -import{Abbr}from"components/Abbr/Abbr"; |
16 | 8 | import{ExternalImage}from"components/ExternalImage/ExternalImage";
|
17 |
| -import{displayError}from"components/GlobalSnackbar/utils"; |
18 | 9 | import{CoderIcon}from"components/Icons/CoderIcon";
|
19 |
| -import{Latency}from"components/Latency/Latency"; |
20 |
| -import{useAuthenticated}from"contexts/auth/RequireAuth"; |
21 | 10 | importtype{ProxyContextValue}from"contexts/ProxyContext";
|
22 |
| -import{BUTTON_SM_HEIGHT,navHeight}from"theme/constants"; |
| 11 | +import{navHeight}from"theme/constants"; |
23 | 12 | import{DeploymentDropdown}from"./DeploymentDropdown";
|
| 13 | +import{ProxyMenu}from"./ProxyMenu"; |
24 | 14 | import{UserDropdown}from"./UserDropdown/UserDropdown";
|
25 | 15 |
|
26 | 16 | exportinterfaceNavbarViewProps{
|
@@ -163,237 +153,6 @@ export const NavbarView: FC<NavbarViewProps> = ({
|
163 | 153 | );
|
164 | 154 | };
|
165 | 155 |
|
166 |
| -interfaceProxyMenuProps{ |
167 |
| -proxyContextValue:ProxyContextValue; |
168 |
| -} |
169 |
| - |
170 |
| -constProxyMenu:FC<ProxyMenuProps>=({ proxyContextValue})=>{ |
171 |
| -consttheme=useTheme(); |
172 |
| -constbuttonRef=useRef<HTMLButtonElement>(null); |
173 |
| -const[isOpen,setIsOpen]=useState(false); |
174 |
| -const[refetchDate,setRefetchDate]=useState<Date>(); |
175 |
| -constselectedProxy=proxyContextValue.proxy.proxy; |
176 |
| -constrefreshLatencies=proxyContextValue.refetchProxyLatencies; |
177 |
| -constcloseMenu=()=>setIsOpen(false); |
178 |
| -constnavigate=useNavigate(); |
179 |
| -constlatencies=proxyContextValue.proxyLatencies; |
180 |
| -constisLoadingLatencies=Object.keys(latencies).length===0; |
181 |
| -constisLoading=proxyContextValue.isLoading||isLoadingLatencies; |
182 |
| -const{ permissions}=useAuthenticated(); |
183 |
| - |
184 |
| -constproxyLatencyLoading=(proxy:TypesGen.Region):boolean=>{ |
185 |
| -if(!refetchDate){ |
186 |
| -// Only show loading if the user manually requested a refetch |
187 |
| -returnfalse; |
188 |
| -} |
189 |
| - |
190 |
| -// Only show a loading spinner if: |
191 |
| -// - A latency exists. This means the latency was fetched at some point, so |
192 |
| -// the loader *should* be resolved. |
193 |
| -// - The proxy is healthy. If it is not, the loader might never resolve. |
194 |
| -// - The latency reported is older than the refetch date. This means the |
195 |
| -// latency is stale and we should show a loading spinner until the new |
196 |
| -// latency is fetched. |
197 |
| -constlatency=latencies[proxy.id]; |
198 |
| -returnproxy.healthy&&latency!==undefined&&latency.at<refetchDate; |
199 |
| -}; |
200 |
| - |
201 |
| -// This endpoint returns a 404 when not using enterprise. |
202 |
| -// If we don't return null, then it looks like this is |
203 |
| -// loading forever! |
204 |
| -if(proxyContextValue.error){ |
205 |
| -returnnull; |
206 |
| -} |
207 |
| - |
208 |
| -if(isLoading){ |
209 |
| -return( |
210 |
| -<Skeleton |
211 |
| -width="110px" |
212 |
| -height={BUTTON_SM_HEIGHT} |
213 |
| -css={{borderRadius:"9999px",transform:"none"}} |
214 |
| -/> |
215 |
| -); |
216 |
| -} |
217 |
| - |
218 |
| -return( |
219 |
| -<> |
220 |
| -<Button |
221 |
| -ref={buttonRef} |
222 |
| -onClick={()=>setIsOpen(true)} |
223 |
| -size="small" |
224 |
| -endIcon={<KeyboardArrowDownOutlined/>} |
225 |
| -css={{ |
226 |
| -"& .MuiSvgIcon-root":{fontSize:14}, |
227 |
| -}} |
228 |
| -> |
229 |
| -<spancss={{ ...visuallyHidden}}> |
230 |
| - Latency for{selectedProxy?.display_name??"your region"} |
231 |
| -</span> |
232 |
| - |
233 |
| -{selectedProxy ?( |
234 |
| -<divcss={{display:"flex",gap:8,alignItems:"center"}}> |
235 |
| -<divcss={{width:16,height:16,lineHeight:0}}> |
236 |
| -<img |
237 |
| -// Empty alt text used because we don't want to double up on |
238 |
| -// screen reader announcements from visually-hidden span |
239 |
| -alt="" |
240 |
| -src={selectedProxy.icon_url} |
241 |
| -css={{ |
242 |
| -objectFit:"contain", |
243 |
| -width:"100%", |
244 |
| -height:"100%", |
245 |
| -}} |
246 |
| -/> |
247 |
| -</div> |
248 |
| - |
249 |
| -<Latency |
250 |
| -latency={latencies?.[selectedProxy.id]?.latencyMS} |
251 |
| -isLoading={proxyLatencyLoading(selectedProxy)} |
252 |
| -/> |
253 |
| -</div> |
254 |
| -) :( |
255 |
| -"Select Proxy" |
256 |
| -)} |
257 |
| -</Button> |
258 |
| - |
259 |
| -<Menu |
260 |
| -open={isOpen} |
261 |
| -anchorEl={buttonRef.current} |
262 |
| -onClick={closeMenu} |
263 |
| -onClose={closeMenu} |
264 |
| -css={{"& .MuiMenu-paper":{paddingTop:8,paddingBottom:8}}} |
265 |
| -// autoFocus here does not affect modal focus; it affects whether the |
266 |
| -// first item in the list will get auto-focus when the menu opens. Have |
267 |
| -// to turn this off because otherwise, screen readers will skip over all |
268 |
| -// the descriptive text and will only have access to the latency options |
269 |
| -autoFocus={false} |
270 |
| -> |
271 |
| -<div |
272 |
| -css={{ |
273 |
| -width:"100%", |
274 |
| -maxWidth:"320px", |
275 |
| -fontSize:14, |
276 |
| -padding:16, |
277 |
| -lineHeight:"140%", |
278 |
| -}} |
279 |
| -> |
280 |
| -<h4 |
281 |
| -autoFocus |
282 |
| -tabIndex={-1} |
283 |
| -css={{ |
284 |
| -fontSize:"inherit", |
285 |
| -fontWeight:600, |
286 |
| -lineHeight:"inherit", |
287 |
| -margin:0, |
288 |
| -marginBottom:4, |
289 |
| -}} |
290 |
| -> |
291 |
| - Select a region nearest to you |
292 |
| -</h4> |
293 |
| - |
294 |
| -<p |
295 |
| -css={{ |
296 |
| -fontSize:13, |
297 |
| -color:theme.palette.text.secondary, |
298 |
| -lineHeight:"inherit", |
299 |
| -marginTop:0.5, |
300 |
| -}} |
301 |
| -> |
302 |
| - Workspace proxies improve terminal and web app connections to |
303 |
| - workspaces. This does not apply to{" "} |
304 |
| -<Abbrtitle="Command-Line Interface"pronunciation="initialism"> |
305 |
| - CLI |
306 |
| -</Abbr>{" "} |
307 |
| - connections. A region must be manually selected, otherwise the |
308 |
| - default primary region will be used. |
309 |
| -</p> |
310 |
| -</div> |
311 |
| - |
312 |
| -<Dividercss={{borderColor:theme.palette.divider}}/> |
313 |
| - |
314 |
| -{proxyContextValue.proxies&& |
315 |
| -[...proxyContextValue.proxies] |
316 |
| -.sort((a,b)=>{ |
317 |
| -constlatencyA=latencies?.[a.id]?.latencyMS??Infinity; |
318 |
| -constlatencyB=latencies?.[b.id]?.latencyMS??Infinity; |
319 |
| -returnlatencyA-latencyB; |
320 |
| -}) |
321 |
| -.map((proxy)=>( |
322 |
| -<MenuItem |
323 |
| -key={proxy.id} |
324 |
| -selected={proxy.id===selectedProxy?.id} |
325 |
| -css={{fontSize:14}} |
326 |
| -onClick={()=>{ |
327 |
| -if(!proxy.healthy){ |
328 |
| -displayError("Please select a healthy workspace proxy."); |
329 |
| -closeMenu(); |
330 |
| -return; |
331 |
| -} |
332 |
| - |
333 |
| -proxyContextValue.setProxy(proxy); |
334 |
| -closeMenu(); |
335 |
| -}} |
336 |
| -> |
337 |
| -<div |
338 |
| -css={{ |
339 |
| -display:"flex", |
340 |
| -gap:24, |
341 |
| -alignItems:"center", |
342 |
| -width:"100%", |
343 |
| -}} |
344 |
| -> |
345 |
| -<divcss={{width:14,height:14,lineHeight:0}}> |
346 |
| -<img |
347 |
| -src={proxy.icon_url} |
348 |
| -alt="" |
349 |
| -css={{ |
350 |
| -objectFit:"contain", |
351 |
| -width:"100%", |
352 |
| -height:"100%", |
353 |
| -}} |
354 |
| -/> |
355 |
| -</div> |
356 |
| - |
357 |
| -{proxy.display_name} |
358 |
| - |
359 |
| -<Latency |
360 |
| -latency={latencies?.[proxy.id]?.latencyMS} |
361 |
| -isLoading={proxyLatencyLoading(proxy)} |
362 |
| -/> |
363 |
| -</div> |
364 |
| -</MenuItem> |
365 |
| -))} |
366 |
| - |
367 |
| -<Dividercss={{borderColor:theme.palette.divider}}/> |
368 |
| - |
369 |
| -{Boolean(permissions.editWorkspaceProxies)&&( |
370 |
| -<MenuItem |
371 |
| -css={{fontSize:14}} |
372 |
| -onClick={()=>{ |
373 |
| -navigate("/deployment/workspace-proxies"); |
374 |
| -}} |
375 |
| -> |
376 |
| - Proxy settings |
377 |
| -</MenuItem> |
378 |
| -)} |
379 |
| - |
380 |
| -<MenuItem |
381 |
| -css={{fontSize:14}} |
382 |
| -onClick={(e)=>{ |
383 |
| -// Stop the menu from closing |
384 |
| -e.stopPropagation(); |
385 |
| -// Refresh the latencies. |
386 |
| -constrefetchDate=refreshLatencies(); |
387 |
| -setRefetchDate(refetchDate); |
388 |
| -}} |
389 |
| -> |
390 |
| - Refresh Latencies |
391 |
| -</MenuItem> |
392 |
| -</Menu> |
393 |
| -</> |
394 |
| -); |
395 |
| -}; |
396 |
| - |
397 | 156 | conststyles={
|
398 | 157 | desktopNavItems:(theme)=>css`
|
399 | 158 | display: none;
|
|