client/web: start using swr for some fetching
Adds swr to the web client, and starts by using it from the useNodeData hook. Updates #10261 Signed-off-by: Sonia Appasamy <sonia@tailscale.com>
This commit is contained in:

committed by
Sonia Appasamy

parent
014ae98297
commit
95655405b8
@ -28,11 +28,10 @@ export default function useAuth() {
|
||||
|
||||
const loadAuth = useCallback(() => {
|
||||
setLoading(true)
|
||||
return apiFetch("/auth", "GET")
|
||||
.then((r) => r.json())
|
||||
return apiFetch<AuthResponse>("/auth", "GET")
|
||||
.then((d) => {
|
||||
setData(d)
|
||||
switch ((d as AuthResponse).authNeeded) {
|
||||
switch (d.authNeeded) {
|
||||
case AuthType.synology:
|
||||
fetch("/webman/login.cgi")
|
||||
.then((r) => r.json())
|
||||
@ -53,15 +52,16 @@ export default function useAuth() {
|
||||
}, [])
|
||||
|
||||
const newSession = useCallback(() => {
|
||||
return apiFetch("/auth/session/new", "GET")
|
||||
.then((r) => r.json())
|
||||
return apiFetch<{ authUrl?: string }>("/auth/session/new", "GET")
|
||||
.then((d) => {
|
||||
if (d.authUrl) {
|
||||
window.open(d.authUrl, "_blank")
|
||||
return apiFetch("/auth/session/wait", "GET")
|
||||
}
|
||||
})
|
||||
.then(() => loadAuth())
|
||||
.then(() => {
|
||||
loadAuth()
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error)
|
||||
})
|
||||
@ -70,7 +70,7 @@ export default function useAuth() {
|
||||
useEffect(() => {
|
||||
loadAuth().then((d) => {
|
||||
if (
|
||||
!d.canManageNode &&
|
||||
!d?.canManageNode &&
|
||||
new URLSearchParams(window.location.search).get("check") === "now"
|
||||
) {
|
||||
newSession()
|
||||
|
@ -33,8 +33,7 @@ export default function useExitNodes(node: NodeData, filter?: string) {
|
||||
const [data, setData] = useState<ExitNode[]>([])
|
||||
|
||||
useEffect(() => {
|
||||
apiFetch("/exit-nodes", "GET")
|
||||
.then((r) => r.json())
|
||||
apiFetch<ExitNode[]>("/exit-nodes", "GET")
|
||||
.then((r) => setData(r))
|
||||
.catch((err) => {
|
||||
alert("Failed operation: " + err.message)
|
||||
|
@ -6,6 +6,7 @@ import { apiFetch, incrementMetric, setUnraidCsrfToken } from "src/api"
|
||||
import { ExitNode, noExitNode, runAsExitNode } from "src/hooks/exit-nodes"
|
||||
import { VersionInfo } from "src/hooks/self-update"
|
||||
import { assertNever } from "src/utils/util"
|
||||
import useSWR from "swr"
|
||||
|
||||
export type NodeData = {
|
||||
Profile: UserProfile
|
||||
@ -120,19 +121,12 @@ type RoutesPOSTData = {
|
||||
|
||||
// useNodeData returns basic data about the current node.
|
||||
export default function useNodeData() {
|
||||
const [data, setData] = useState<NodeData>()
|
||||
const { data, mutate } = useSWR<NodeData>("/data")
|
||||
const [isPosting, setIsPosting] = useState<boolean>(false)
|
||||
|
||||
const refreshData = useCallback(
|
||||
() =>
|
||||
apiFetch("/data", "GET")
|
||||
.then((r) => r.json())
|
||||
.then((d: NodeData) => {
|
||||
setData(d)
|
||||
setUnraidCsrfToken(d.IsUnraid ? d.UnraidToken : undefined)
|
||||
})
|
||||
.catch((error) => console.error(error)),
|
||||
[setData]
|
||||
useEffect(
|
||||
() => setUnraidCsrfToken(data?.IsUnraid ? data.UnraidToken : undefined),
|
||||
[data]
|
||||
)
|
||||
|
||||
const prefsPATCH = useCallback(
|
||||
@ -147,12 +141,12 @@ export default function useNodeData() {
|
||||
// then make the prefs PATCH. If the request fails,
|
||||
// data will be updated to it's previous value in
|
||||
// onComplete below.
|
||||
setData(optimisticUpdates)
|
||||
mutate(optimisticUpdates, false)
|
||||
}
|
||||
|
||||
const onComplete = () => {
|
||||
setIsPosting(false)
|
||||
refreshData() // refresh data after PATCH finishes
|
||||
mutate() // refresh data after PATCH finishes
|
||||
}
|
||||
|
||||
return apiFetch("/local/v0/prefs", "PATCH", d)
|
||||
@ -163,7 +157,7 @@ export default function useNodeData() {
|
||||
throw err
|
||||
})
|
||||
},
|
||||
[setIsPosting, refreshData, setData, data]
|
||||
[data, mutate]
|
||||
)
|
||||
|
||||
const routesPOST = useCallback(
|
||||
@ -171,7 +165,7 @@ export default function useNodeData() {
|
||||
setIsPosting(true)
|
||||
const onComplete = () => {
|
||||
setIsPosting(false)
|
||||
refreshData() // refresh data after POST finishes
|
||||
mutate() // refresh data after POST finishes
|
||||
}
|
||||
const updateMetrics = () => {
|
||||
// only update metrics if values have changed
|
||||
@ -195,26 +189,7 @@ export default function useNodeData() {
|
||||
throw err
|
||||
})
|
||||
},
|
||||
[setIsPosting, refreshData, data?.AdvertisingExitNode]
|
||||
)
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
// Initial data load.
|
||||
refreshData()
|
||||
|
||||
// Refresh on browser tab focus.
|
||||
const onVisibilityChange = () => {
|
||||
document.visibilityState === "visible" && refreshData()
|
||||
}
|
||||
window.addEventListener("visibilitychange", onVisibilityChange)
|
||||
return () => {
|
||||
// Cleanup browser tab listener.
|
||||
window.removeEventListener("visibilitychange", onVisibilityChange)
|
||||
}
|
||||
},
|
||||
// Run once.
|
||||
[refreshData]
|
||||
[mutate, data?.AdvertisingExitNode]
|
||||
)
|
||||
|
||||
const nodeUpdaters: NodeUpdaters = useMemo(
|
||||
@ -245,5 +220,5 @@ export default function useNodeData() {
|
||||
]
|
||||
)
|
||||
|
||||
return { data, refreshData, nodeUpdaters, isPosting }
|
||||
return { data, refreshData: mutate, nodeUpdaters, isPosting }
|
||||
}
|
||||
|
@ -61,9 +61,8 @@ export function useInstallUpdate(currentVersion: string, cv?: VersionInfo) {
|
||||
let timer: NodeJS.Timeout | undefined
|
||||
|
||||
function poll() {
|
||||
apiFetch("/local/v0/update/progress", "GET")
|
||||
.then((res) => res.json())
|
||||
.then((res: UpdateProgress[]) => {
|
||||
apiFetch<UpdateProgress[]>("/local/v0/update/progress", "GET")
|
||||
.then((res) => {
|
||||
// res contains a list of UpdateProgresses that is strictly increasing
|
||||
// in size, so updateMessagesRead keeps track (across calls of poll())
|
||||
// of how many of those we have already read. This is why it is not
|
||||
|
Reference in New Issue
Block a user