mirror of
https://github.com/coder/coder.git
synced 2025-07-13 21:36:50 +00:00
feat: add spinner to latencies when refetching (#8278)
* feat: add spinner to latencies when refetching
This commit is contained in:
@ -29,8 +29,8 @@ const Template: Story<AppLinkProps> = (args) => (
|
||||
clearProxy: () => {
|
||||
return
|
||||
},
|
||||
refetchProxyLatencies: () => {
|
||||
return
|
||||
refetchProxyLatencies: (): Date => {
|
||||
return new Date()
|
||||
},
|
||||
}}
|
||||
>
|
||||
|
@ -188,6 +188,7 @@ const ProxyMenu: FC<{ proxyContextValue: ProxyContextValue }> = ({
|
||||
}) => {
|
||||
const buttonRef = useRef<HTMLButtonElement>(null)
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const [refetchDate, setRefetchDate] = useState<Date>()
|
||||
const selectedProxy = proxyContextValue.proxy.proxy
|
||||
const refreshLatencies = proxyContextValue.refetchProxyLatencies
|
||||
const closeMenu = () => setIsOpen(false)
|
||||
@ -196,6 +197,26 @@ const ProxyMenu: FC<{ proxyContextValue: ProxyContextValue }> = ({
|
||||
const isLoadingLatencies = Object.keys(latencies).length === 0
|
||||
const isLoading = proxyContextValue.isLoading || isLoadingLatencies
|
||||
const permissions = usePermissions()
|
||||
const proxyLatencyLoading = (proxy: TypesGen.Region): boolean => {
|
||||
if (!refetchDate) {
|
||||
// Only show loading if the user manually requested a refetch
|
||||
return false
|
||||
}
|
||||
|
||||
const latency = latencies?.[proxy.id]
|
||||
// Only show a loading spinner if:
|
||||
// - A latency exists. This means the latency was fetched at some point, so the
|
||||
// loader *should* be resolved.
|
||||
// - The proxy is healthy. If it is not, the loader might never resolve.
|
||||
// - The latency reported is older than the refetch date. This means the latency
|
||||
// is stale and we should show a loading spinner until the new latency is
|
||||
// fetched.
|
||||
if (proxy.healthy && latency && latency.at < refetchDate) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
@ -234,6 +255,7 @@ const ProxyMenu: FC<{ proxyContextValue: ProxyContextValue }> = ({
|
||||
{selectedProxy.display_name}
|
||||
<ProxyStatusLatency
|
||||
latency={latencies?.[selectedProxy.id]?.latencyMS}
|
||||
isLoading={proxyLatencyLoading(selectedProxy)}
|
||||
/>
|
||||
</Box>
|
||||
) : (
|
||||
@ -277,7 +299,10 @@ const ProxyMenu: FC<{ proxyContextValue: ProxyContextValue }> = ({
|
||||
/>
|
||||
</Box>
|
||||
{proxy.display_name}
|
||||
<ProxyStatusLatency latency={latencies?.[proxy.id]?.latencyMS} />
|
||||
<ProxyStatusLatency
|
||||
latency={latencies?.[proxy.id]?.latencyMS}
|
||||
isLoading={proxyLatencyLoading(proxy)}
|
||||
/>
|
||||
</Box>
|
||||
</MenuItem>
|
||||
))}
|
||||
@ -298,7 +323,8 @@ const ProxyMenu: FC<{ proxyContextValue: ProxyContextValue }> = ({
|
||||
// Stop the menu from closing
|
||||
e.stopPropagation()
|
||||
// Refresh the latencies.
|
||||
refreshLatencies()
|
||||
const refetchDate = refreshLatencies()
|
||||
setRefetchDate(refetchDate)
|
||||
}}
|
||||
>
|
||||
Refresh Latencies
|
||||
|
@ -4,11 +4,29 @@ import Box from "@mui/material/Box"
|
||||
import Tooltip from "@mui/material/Tooltip"
|
||||
import { FC } from "react"
|
||||
import { getLatencyColor } from "utils/latency"
|
||||
import CircularProgress from "@mui/material/CircularProgress"
|
||||
|
||||
export const ProxyStatusLatency: FC<{ latency?: number }> = ({ latency }) => {
|
||||
export const ProxyStatusLatency: FC<{
|
||||
latency?: number
|
||||
isLoading?: boolean
|
||||
}> = ({ latency, isLoading }) => {
|
||||
const theme = useTheme()
|
||||
const color = getLatencyColor(theme, latency)
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<Tooltip title="Loading latency...">
|
||||
<CircularProgress
|
||||
size="14px"
|
||||
sx={{
|
||||
// Always use the no latency color for loading.
|
||||
color: getLatencyColor(theme, undefined),
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
if (!latency) {
|
||||
return (
|
||||
<Tooltip title="Latency not available">
|
||||
|
@ -68,8 +68,8 @@ const TemplateFC = (
|
||||
clearProxy: () => {
|
||||
return
|
||||
},
|
||||
refetchProxyLatencies: () => {
|
||||
return
|
||||
refetchProxyLatencies: (): Date => {
|
||||
return new Date()
|
||||
},
|
||||
}}
|
||||
>
|
||||
|
@ -33,8 +33,8 @@ Example.args = {
|
||||
clearProxy: () => {
|
||||
return
|
||||
},
|
||||
refetchProxyLatencies: () => {
|
||||
return
|
||||
refetchProxyLatencies: (): Date => {
|
||||
return new Date()
|
||||
},
|
||||
}}
|
||||
>
|
||||
@ -106,8 +106,8 @@ BunchOfMetadata.args = {
|
||||
clearProxy: () => {
|
||||
return
|
||||
},
|
||||
refetchProxyLatencies: () => {
|
||||
return
|
||||
refetchProxyLatencies: (): Date => {
|
||||
return new Date()
|
||||
},
|
||||
}}
|
||||
>
|
||||
|
@ -42,8 +42,8 @@ const meta: Meta<typeof Workspace> = {
|
||||
setProxy: () => {
|
||||
return
|
||||
},
|
||||
refetchProxyLatencies: () => {
|
||||
return
|
||||
refetchProxyLatencies: (): Date => {
|
||||
return new Date()
|
||||
},
|
||||
}}
|
||||
>
|
||||
|
@ -53,7 +53,7 @@ export interface ProxyContextValue {
|
||||
proxyLatencies: Record<string, ProxyLatencyReport>
|
||||
// refetchProxyLatencies will trigger refreshing of the proxy latencies. By default the latencies
|
||||
// are loaded once.
|
||||
refetchProxyLatencies: () => void
|
||||
refetchProxyLatencies: () => Date
|
||||
// setProxy is a function that sets the user's selected proxy. This function should
|
||||
// only be called if the user is manually selecting a proxy. This value is stored in local
|
||||
// storage and will persist across reloads and tabs.
|
||||
|
Reference in New Issue
Block a user