forked from jordanlambrecht/tracker-tracker
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathuseUpdateCheck.ts
More file actions
86 lines (72 loc) · 2.65 KB
/
useUpdateCheck.ts
File metadata and controls
86 lines (72 loc) · 2.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// src/hooks/useUpdateCheck.ts
//
// Functions: useUpdateCheck, compareVersions
import { useEffect, useRef, useState } from "react"
const GITHUB_REPO = "jordanlambrecht/tracker-tracker"
interface UpdateCheckResult {
latestVersion: string | null
updateAvailable: boolean
loading: boolean
}
/**
* Compares two semver strings. Returns > 0 if latest is newer, 0 if equal, < 0 if current is newer.
*/
export function compareVersions(current: string, latest: string): number {
// Strip v prefix and pre-release/build metadata (i.e, "1.3.0-beta.1+build" → "1.3.0")
const clean = (s: string) => s.replace(/^v/, "").split(/[-+]/)[0]
const a = clean(current).split(".").map(Number)
const b = clean(latest).split(".").map(Number)
for (let i = 0; i < Math.max(a.length, b.length); i++) {
const diff = (b[i] ?? 0) - (a[i] ?? 0)
if (diff !== 0) return diff
}
return 0
}
/**
* Checks GitHub for a newer version. Runs once per session (cached via ref).
* Tries Releases API first, falls back to Tags API.
*/
export function useUpdateCheck(): UpdateCheckResult {
const [latestVersion, setLatestVersion] = useState<string | null>(null)
const [loading, setLoading] = useState(true)
const checkedRef = useRef(false)
useEffect(() => {
if (checkedRef.current) return
checkedRef.current = true
async function check() {
try {
// Try GitHub Releases API first (has explicit "latest" concept)
const res = await fetch(`https://api.github.com/repos/${GITHUB_REPO}/releases/latest`, {
headers: { Accept: "application/vnd.github.v3+json" },
})
if (res.ok) {
const data = await res.json()
const tag = (data.tag_name as string)?.replace(/^v/, "")
if (tag) {
setLatestVersion(tag)
return
}
}
// Fall back to Tags API (created by `pnpm version` + `git push --tags`)
const tagRes = await fetch(`https://api.github.com/repos/${GITHUB_REPO}/tags?per_page=1`, {
headers: { Accept: "application/vnd.github.v3+json" },
})
if (tagRes.ok) {
const tags = (await tagRes.json()) as { name: string }[]
if (tags.length > 0) {
setLatestVersion(tags[0].name.replace(/^v/, ""))
}
}
} catch {
// Network error (offline, blocked, private repo) — silently skip
} finally {
setLoading(false)
}
}
check()
}, [])
const currentVersion = process.env.NEXT_PUBLIC_APP_VERSION ?? "0.0.0"
const updateAvailable =
latestVersion !== null && compareVersions(currentVersion, latestVersion) > 0
return { latestVersion, updateAvailable, loading }
}