refactor(freshness): drop time-based stale-known status
The 2-minute timer + tick ref decayed verified to stale-known and rendered a clock icon, but the user can always click the badge to re-check. Removing the timer simplifies the hook and the badge has one fewer visual state.
This commit is contained in:
@@ -13,9 +13,6 @@ defineEmits<{ (e: "click"): void }>()
|
|||||||
const formatTime = (d: Date) =>
|
const formatTime = (d: Date) =>
|
||||||
d.toLocaleTimeString(undefined, { hour: "2-digit", minute: "2-digit" })
|
d.toLocaleTimeString(undefined, { hour: "2-digit", minute: "2-digit" })
|
||||||
|
|
||||||
const minutesAgo = (d: Date) =>
|
|
||||||
Math.max(1, Math.round((Date.now() - d.getTime()) / 60000))
|
|
||||||
|
|
||||||
const label = computed(() => {
|
const label = computed(() => {
|
||||||
switch (props.status) {
|
switch (props.status) {
|
||||||
case "verified":
|
case "verified":
|
||||||
@@ -24,10 +21,6 @@ const label = computed(() => {
|
|||||||
return "Checking…"
|
return "Checking…"
|
||||||
case "outdated":
|
case "outdated":
|
||||||
return "Outdated"
|
return "Outdated"
|
||||||
case "stale-known":
|
|
||||||
return props.lastCheckedAt
|
|
||||||
? `Checked ${minutesAgo(props.lastCheckedAt)}m ago`
|
|
||||||
: "Not checked"
|
|
||||||
case "offline":
|
case "offline":
|
||||||
return "Can’t reach GitHub"
|
return "Can’t reach GitHub"
|
||||||
case "unknown":
|
case "unknown":
|
||||||
@@ -44,8 +37,6 @@ const tooltip = computed(() => {
|
|||||||
: "Click to re-check."
|
: "Click to re-check."
|
||||||
case "outdated":
|
case "outdated":
|
||||||
return "GitHub has a newer version. Click to pull latest."
|
return "GitHub has a newer version. Click to pull latest."
|
||||||
case "stale-known":
|
|
||||||
return "Click to verify against GitHub."
|
|
||||||
case "offline":
|
case "offline":
|
||||||
return "Could not reach GitHub. Click to retry."
|
return "Could not reach GitHub. Click to retry."
|
||||||
case "checking":
|
case "checking":
|
||||||
@@ -88,14 +79,9 @@ const isBusy = computed(() => props.status === "checking")
|
|||||||
<path d="M15 19l2 2l4 -4" />
|
<path d="M15 19l2 2l4 -4" />
|
||||||
</svg>
|
</svg>
|
||||||
<svg
|
<svg
|
||||||
v-else-if="status === 'unknown' || status === 'stale-known'"
|
v-else-if="status === 'unknown'"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="icon icon-tabler"
|
class="icon icon-tabler icon-tabler-cloud-question"
|
||||||
:class="
|
|
||||||
status === 'stale-known'
|
|
||||||
? 'icon-tabler-clock'
|
|
||||||
: 'icon-tabler-cloud-question'
|
|
||||||
"
|
|
||||||
width="20"
|
width="20"
|
||||||
height="20"
|
height="20"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
@@ -105,19 +91,13 @@ const isBusy = computed(() => props.status === "checking")
|
|||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
>
|
>
|
||||||
<template v-if="status === 'stale-known'">
|
<path
|
||||||
<path d="M3 12a9 9 0 1 0 18 0a9 9 0 0 0 -18 0" />
|
d="M14.5 18.004h-7.843c-2.572 -.004 -4.657 -2.011 -4.657 -4.487c0 -2.475 2.085 -4.482 4.657 -4.482c.393 -1.762 1.794 -3.2 3.675 -3.773c1.88 -.572 3.956 -.193 5.444 1c1.488 1.19 2.162 3.007 1.77 4.769h.99"
|
||||||
<path d="M12 7v5l3 3" />
|
/>
|
||||||
</template>
|
<path d="M19 22v.01" />
|
||||||
<template v-else>
|
<path
|
||||||
<path
|
d="M19 19a2.003 2.003 0 0 0 .914 -3.782a1.98 1.98 0 0 0 -2.414 .483"
|
||||||
d="M14.5 18.004h-7.843c-2.572 -.004 -4.657 -2.011 -4.657 -4.487c0 -2.475 2.085 -4.482 4.657 -4.482c.393 -1.762 1.794 -3.2 3.675 -3.773c1.88 -.572 3.956 -.193 5.444 1c1.488 1.19 2.162 3.007 1.77 4.769h.99"
|
/>
|
||||||
/>
|
|
||||||
<path d="M19 22v.01" />
|
|
||||||
<path
|
|
||||||
d="M19 19a2.003 2.003 0 0 0 .914 -3.782a1.98 1.98 0 0 0 -2.414 .483"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</svg>
|
</svg>
|
||||||
<svg
|
<svg
|
||||||
v-else-if="status === 'outdated'"
|
v-else-if="status === 'outdated'"
|
||||||
@@ -212,7 +192,6 @@ const isBusy = computed(() => props.status === "checking")
|
|||||||
}
|
}
|
||||||
|
|
||||||
.state-unknown,
|
.state-unknown,
|
||||||
.state-stale-known,
|
|
||||||
.state-checking {
|
.state-checking {
|
||||||
color: var(--color-base-content);
|
color: var(--color-base-content);
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { computed, Ref, ref } from "vue"
|
import { Ref, ref } from "vue"
|
||||||
|
|
||||||
import { useGitHubContent } from "@/hooks/useGitHubContent.hook"
|
import { useGitHubContent } from "@/hooks/useGitHubContent.hook"
|
||||||
import { markdownBuilder } from "@/hooks/useMarkdown.hook"
|
import { markdownBuilder } from "@/hooks/useMarkdown.hook"
|
||||||
@@ -10,12 +10,9 @@ export type FreshnessStatus =
|
|||||||
| "unknown"
|
| "unknown"
|
||||||
| "checking"
|
| "checking"
|
||||||
| "verified"
|
| "verified"
|
||||||
| "stale-known"
|
|
||||||
| "outdated"
|
| "outdated"
|
||||||
| "offline"
|
| "offline"
|
||||||
|
|
||||||
const STALE_AFTER_MS = 2 * 60 * 1000
|
|
||||||
|
|
||||||
export const useNoteFreshness = ({
|
export const useNoteFreshness = ({
|
||||||
user,
|
user,
|
||||||
repo,
|
repo,
|
||||||
@@ -32,54 +29,36 @@ export const useNoteFreshness = ({
|
|||||||
const store = useUserRepoStore()
|
const store = useUserRepoStore()
|
||||||
const { fetchLatestSha } = useGitHubContent({ user, repo })
|
const { fetchLatestSha } = useGitHubContent({ user, repo })
|
||||||
|
|
||||||
const rawStatus = ref<FreshnessStatus>("unknown")
|
const status = ref<FreshnessStatus>("unknown")
|
||||||
const lastCheckedAt = ref<Date | null>(null)
|
const lastCheckedAt = ref<Date | null>(null)
|
||||||
const latestSha = ref<string | null>(null)
|
const latestSha = ref<string | null>(null)
|
||||||
const tick = ref(0)
|
|
||||||
let staleTimer: ReturnType<typeof setTimeout> | null = null
|
|
||||||
|
|
||||||
const status = computed<FreshnessStatus>(() => {
|
|
||||||
void tick.value
|
|
||||||
if (rawStatus.value !== "verified") return rawStatus.value
|
|
||||||
if (!lastCheckedAt.value) return rawStatus.value
|
|
||||||
const age = Date.now() - lastCheckedAt.value.getTime()
|
|
||||||
return age > STALE_AFTER_MS ? "stale-known" : "verified"
|
|
||||||
})
|
|
||||||
|
|
||||||
const armStaleTimer = () => {
|
|
||||||
if (staleTimer) clearTimeout(staleTimer)
|
|
||||||
staleTimer = setTimeout(() => {
|
|
||||||
tick.value++
|
|
||||||
}, STALE_AFTER_MS + 100)
|
|
||||||
}
|
|
||||||
|
|
||||||
const expectedSha = async () => (await getEditedSha()) ?? sha.value
|
const expectedSha = async () => (await getEditedSha()) ?? sha.value
|
||||||
|
|
||||||
const check = async () => {
|
const check = async () => {
|
||||||
if (!path.value) return
|
if (!path.value) return
|
||||||
rawStatus.value = "checking"
|
status.value = "checking"
|
||||||
const remoteSha = await fetchLatestSha(path.value)
|
const remoteSha = await fetchLatestSha(path.value)
|
||||||
if (remoteSha === null) {
|
if (remoteSha === null) {
|
||||||
rawStatus.value = "offline"
|
status.value = "offline"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
latestSha.value = remoteSha
|
latestSha.value = remoteSha
|
||||||
lastCheckedAt.value = new Date()
|
lastCheckedAt.value = new Date()
|
||||||
const local = await expectedSha()
|
const local = await expectedSha()
|
||||||
rawStatus.value = remoteSha === local ? "verified" : "outdated"
|
status.value = remoteSha === local ? "verified" : "outdated"
|
||||||
armStaleTimer()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const pullLatest = async (): Promise<string | null> => {
|
const pullLatest = async (): Promise<string | null> => {
|
||||||
if (!path.value) return null
|
if (!path.value) return null
|
||||||
const remoteSha = latestSha.value ?? (await fetchLatestSha(path.value))
|
const remoteSha = latestSha.value ?? (await fetchLatestSha(path.value))
|
||||||
if (!remoteSha) {
|
if (!remoteSha) {
|
||||||
rawStatus.value = "offline"
|
status.value = "offline"
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
const fileContent = await queryFileContent(user, repo, remoteSha)
|
const fileContent = await queryFileContent(user, repo, remoteSha)
|
||||||
if (!fileContent) {
|
if (!fileContent) {
|
||||||
rawStatus.value = "offline"
|
status.value = "offline"
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
const { saveCacheNote } = prepareNoteCache(sha.value, path.value)
|
const { saveCacheNote } = prepareNoteCache(sha.value, path.value)
|
||||||
@@ -90,8 +69,7 @@ export const useNoteFreshness = ({
|
|||||||
store.addFile({ path: path.value, sha: remoteSha })
|
store.addFile({ path: path.value, sha: remoteSha })
|
||||||
latestSha.value = remoteSha
|
latestSha.value = remoteSha
|
||||||
lastCheckedAt.value = new Date()
|
lastCheckedAt.value = new Date()
|
||||||
rawStatus.value = "verified"
|
status.value = "verified"
|
||||||
armStaleTimer()
|
|
||||||
const { getRawContent } = markdownBuilder(sha.value)
|
const { getRawContent } = markdownBuilder(sha.value)
|
||||||
return getRawContent(fileContent)
|
return getRawContent(fileContent)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user